#region Using

using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Web.Configuration;

#endregion

namespace BlogEngine.Core.Providers
{
    /// <summary>
    /// The proxy class for communication between
    /// the business objects and the providers.
    /// </summary>
    public static class BlogService
    {

        #region Provider model

        private static BlogProviderBase _provider;
        private static BlogProviderCollection _providers;
        private static object _lock = new object();

        /// <summary>
        /// Gets the current provider.
        /// </summary>
        public static BlogProviderBase Provider
        {
            get { return _provider; }
        }

        /// <summary>
        /// Gets a collection of all registered providers.
        /// </summary>
        public static BlogProviderCollection Providers
        {
            get { return _providers; }
        }

        /// <summary>
        /// Load the providers from the web.config.
        /// </summary>
        private static void LoadProviders()
        {
            // Avoid claiming lock if providers are already loaded
            if (_provider == null)
            {
                lock (_lock)
                {
                    // Do this again to make sure _provider is still null
                    if (_provider == null)
                    {
                        // Get a reference to the <blogProvider> section
                        BlogProviderSection section = (BlogProviderSection)WebConfigurationManager.GetSection("BlogEngine/blogProvider");

                        // Load registered providers and point _provider
                        // to the default provider
                        _providers = new BlogProviderCollection();
                        ProvidersHelper.InstantiateProviders(section.Providers, _providers, typeof(BlogProviderBase));
                        _provider = _providers[section.DefaultProvider];

                        if (_provider == null)
                            throw new ProviderException("Unable to load default BlogProvider");
                    }
                }
            }
        }

        #endregion

        #region Generic mechanism

        private static string ConvertType(Type type)
        {
            if (type == typeof(Post))
                return "Post";
            else if (type == typeof(Page))
                return "Page";
            else if (type == typeof(Link))
                return "Link";
            else if (type == typeof(Category))
                return "Category";            
            else
                throw new ArgumentOutOfRangeException("type is not supported by the blogservice");
        }
        
        /// <summary>
        /// Generics based fill method allowing the base class to invoke the provider without 
        /// knowing the exact method name.
        /// </summary>
        /// <typeparam name="TYPE"></typeparam>
        /// <returns></returns>
        public static List<TYPE> Fill<TYPE>() where TYPE : new()
        {
            Type fillType = typeof(TYPE);
            LoadProviders();

            switch (BlogService.ConvertType(typeof(TYPE)))
            {
                case "Post":
                    return _provider.FillPosts().ConvertAll<TYPE>(delegate(Post item) { return (TYPE)(object)item; });
                case "Page":
                    return _provider.FillPages().ConvertAll<TYPE>(delegate(Page item) { return (TYPE)(object)item; });
                case "Link":
                    return _provider.FillLinks().ConvertAll<TYPE>(delegate(Link item) { return (TYPE)(object)item; });
                case "Category":
                    return _provider.FillCategories().ConvertAll<TYPE>(delegate(Category item) { return (TYPE)(object)item; });              
				default:
                    return new List<TYPE>();
            }
        }

        public static void Delete<TYPE>(TYPE item) where TYPE : new()
        {
            Type fillType = typeof(TYPE);
            LoadProviders();

            switch (BlogService.ConvertType(typeof(TYPE)))
            {
                case "Post":
                    _provider.DeletePost(((Post)(object)item));
                    break;
                case "Page":
                    _provider.DeletePage(((Page)(object)item));
                    break;
                case "Link":
                    _provider.DeleteLink(((Link)(object)item));
                    break;
                case "Category":
                    _provider.DeleteCategory(((Category)(object)item));
                    break;
                default:
                    break;
            }
        }

        public static void Insert<TYPE>(TYPE item) where TYPE : new()
        {
            Type fillType = typeof(TYPE);
            LoadProviders();

            switch (BlogService.ConvertType(typeof(TYPE)))
            {
                case "Post":
                    _provider.InsertPost(((Post)(object)item));
                    break;
                case "Page":
                    _provider.InsertPage(((Page)(object)item));
                    break;
                case "Link":
                    _provider.InsertLink(((Link)(object)item));
                    break;
                case "Category":
                    _provider.InsertCategory(((Category)(object)item));
                    break;
                default:
                    break;
            }
        }
        public static void Update<TYPE>(TYPE item) where TYPE : new()
        {
            Type fillType = typeof(TYPE);
            LoadProviders();

            switch (BlogService.ConvertType(typeof(TYPE)))
            {
                case "Post":
                    _provider.UpdatePost(((Post)(object)item));
                    break;
                case "Page":
                    _provider.UpdatePage(((Page)(object)item));
                    break;
                case "Link":
                    _provider.UpdateLink(((Link)(object)item));
                    break;
                case "Category":
                    _provider.UpdateCategory(((Category)(object)item));
                    break;
                default:
                    break;
            }
        }
        public static TYPE Select<TYPE,KEY>(KEY id) where TYPE : new()
        {
            Type fillType = typeof(TYPE);
            LoadProviders();

            switch (BlogService.ConvertType(typeof(TYPE)))
            {
                case "Post":
                    return (TYPE)(object)_provider.SelectPost(((Guid)(object)id));
                case "Page":
                    return (TYPE)(object)_provider.SelectPage(((Guid)(object)id));
                case "Link":
                    return (TYPE)(object)_provider.SelectLink(((Guid)(object)id));
                case "Category":
                    return (TYPE)(object)_provider.SelectCategory(((Guid)(object)id));
                default:
                    return new TYPE();
            }
        }

        #endregion

        /// <summary>
        /// Returns an IRateable Object based on a GUID without
        /// reference to the objects underlying type (e.g. Post, Photo).
        /// 
        /// Attempts to find the item in this order:
        ///   1. Post
        ///   2. Page
        ///   3. Photo
        ///   4. Album
        ///   5. Video
        /// </summary>
        /// <remarks>
        /// This is possible because the chance of a collision between guids is pretty low,
        /// unless, there is a serious fault in the application
        /// </remarks>
        /// <param name="id">Guid representing the object</param>
        /// <returns></returns>
        public static IRateable GetRateable(Guid id)
        {
            LoadProviders();

            //try post as most likely to be rated
            IRateable item = Post.GetById(id);

            if (item == null)
                item = Page.GetById(id);

            return item;
        }

        #region Settings

        /// <summary>
        /// Loads the settings from the provider and returns
        /// them in a StringDictionary for the BlogSettings class to use.
        /// </summary>
        public static System.Collections.Specialized.StringDictionary LoadSettings()
        {
            LoadProviders();
            return _provider.LoadSettings();
        }

        /// <summary>
        /// Save the settings to the current provider.
        /// </summary>
        public static void SaveSettings(System.Collections.Specialized.StringDictionary settings)
        {
            LoadProviders();
            _provider.SaveSettings(settings);
        }

        #endregion

        #region Ping services

        /// <summary>
        /// Loads the ping services.
        /// </summary>
        public static StringCollection LoadPingServices()
        {
            LoadProviders();
            return _provider.LoadPingServices();
        }

        /// <summary>
        /// Saves the ping services.
        /// </summary>
        /// <param name="services">The services.</param>
        public static void SavePingServices(StringCollection services)
        {
            LoadProviders();
            _provider.SavePingServices(services);
        }

        #endregion

		#region Stop words

		/// <summary>
		/// Loads the stop words from the data store.
		/// </summary>
		public static StringCollection LoadStopWords()
		{
			LoadProviders();
			return _provider.LoadStopWords();
		}

		#endregion

        #region Extension Settings

        //Extension Settings
        /// <summary>
        /// Loads the extension settings to the provider.
        /// </summary>
        /// <returns></returns>
        public static Stream LoadExtensionSettings()
        {
            LoadProviders();
            return _provider.LoadExtensionSettings();
        }

        /// <summary>
        /// Saves the extension settings to the provider.
        /// </summary>
        /// <param name="settings">The settings.</param>
        public static void SaveExtensionSettings(Stream settings)
        {
            LoadProviders();
            _provider.SaveExtensionSettings(settings);
        }

        #endregion
    }
}
